package com.example.shoputils.id;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @Author 刘武贵
 * @Date 2024/2/26 23:57
 * @Description 功能描述：雪花算法工厂
 * 为了防止每次使用SnowFlake类时都会新建一个对象，
 * 这里新建SnowFlakeFactory类，作为SnowFlake的简单工厂类，在SnowFlakeFactory类中，
 * 主要是定义了一个ConcurrentMap类型的成员变量snowFlakeCache用来缓存SnowFlake类的对象，这样就不用在使用SnowFlake类时，每次都要新建一个类对象了。
 *
 * 也许有小伙伴会问：不就是新建一个对象嘛，为啥还要缓存起来呢。
 *
 * 其实，在普通场景下，新建不新建对象，缓存不缓存对象几乎没啥影响，但是在高并发、大流量的场景下，如果每次都创建对象的话，系统的性能与资源损耗还是比较大的。
 *
 * 在SnowFlakeFactory类中，主要对外提供了两个获取SnowFlake的方法，
 * 一个是getSnowFlakeFromCache()方法，另一个是getSnowFlakeByDataCenterIdAndMachineIdFromCache()方法。
 */
public class SnowFlakeFactory {

    /**
     * 默认数据中心id
     */
    private static final long DEFAULT_DATACENTER_ID = 1;

    /**
     * 默认的机器id
     */
    private static final long DEFAULT_MACHINE_ID = 1;

    /**
     * 默认的雪花算法句柄
     */
    private static final String DEFAULT_SNOW_FLAKE = "snow_flake";

    /**
     * 缓存SnowFlake对象
     */
    private static ConcurrentMap<String, SnowFlake> snowFlakeCache = new ConcurrentHashMap<>(2);

    /**
     * 根据数据中心ID和机器ID获取SnowFlake实例
     * @param datacenterId 数据中心ID
     * @param machineId 机器ID
     * @return SnowFlake实例
     */
    private static SnowFlake getSnowFlake(long datacenterId, long machineId) {
        return new SnowFlake(datacenterId, machineId);
    }

    /**
     * 根据默认数据中心ID和默认机器ID获取SnowFlake实例
     * @return SnowFlake实例
     */
    private static SnowFlake getSnowFlake() {
        return new SnowFlake(DEFAULT_DATACENTER_ID, DEFAULT_MACHINE_ID);
    }

    /**
     * 从缓存中获取SnowFlake实例，如果缓存中不存在则创建新的实例并放入缓存中
     *
     * 在snowFlakeCache缓存中获取默认的SnowFlake对象实例，如果对象不存在，
     * 则调用SnowFlake类的构造方法，并且传入默认的数据中心id和机器id，将实例化后的SnowFlake对象加入缓存，并且返回SnowFlake对象
     *
     * @return SnowFlake实例
     */
    public static SnowFlake getSnowFlakeFromCache() {
        SnowFlake snowFlake = snowFlakeCache.get(DEFAULT_SNOW_FLAKE);
        if(snowFlake == null) {
            snowFlake = new SnowFlake(DEFAULT_DATACENTER_ID, DEFAULT_MACHINE_ID);
            snowFlakeCache.put(DEFAULT_SNOW_FLAKE, snowFlake);
        }
        return snowFlake;
    }

    /**
     * 根据数据中心id和机器id从缓存中获取全局id
     *
     * getSnowFlakeByDataCenterIdAndMachineIdFromCache()方法提供了两个参数，一个是Long类型的dataCenterId，
     * 表示数据中心或者机房的id，一个是Long类型的machineId，表示机器id或者服务所在的服务器id。
     *
     * 在getSnowFlakeByDataCenterIdAndMachineIdFromCache()方法中，会对传入的两个参数进行限制。然后生成缓存SnowFlake对象实例的缓存Key，
     * 根据生成的Key到snowFlakeCache缓存中获取SnowFlake对象实例，如果对象实例不存在，
     * 则根据传入的dataCenterId和machineId生成SnowFlake对象实例，并放入snowFlakeCache缓存中，最后返回SnowFlake对象实例
     *
     * @param dataCenterId: 数据中心或者机房的id 取值为1~31
     * @param machineId: 机器id或者服务所在的服务器id 取值为1~31
     */
    public static SnowFlake getSnowFlakeByDataCenterIdAndMachineIdFromCache(Long dataCenterId, Long machineId) {
        if (dataCenterId > SnowFlake.getMaxDataCeneterNum() || dataCenterId < 0) {
            throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }
        if (machineId > SnowFlake.getMaxMachineNum() || machineId < 0) {
            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }
        String key = DEFAULT_SNOW_FLAKE.concat("_").concat(String.valueOf(dataCenterId)).concat("_").concat(String.valueOf(machineId));
        SnowFlake snowFlake = snowFlakeCache.get(key);
        if(snowFlake == null) {
            snowFlake = new SnowFlake(dataCenterId, machineId);
            snowFlakeCache.put(key, snowFlake);
        }
        return snowFlake;
    }

    /**
     * 主函数入口
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        for(int i = 0; i < 100; i++) {
            // 从缓存中获取Snowflake实例
            System.out.println(getSnowFlakeFromCache().nextId());
        }
    }
}
